From 012386f781507e28d71fc3affd14d4b03e7883d6 Mon Sep 17 00:00:00 2001 From: robertl Date: Thu, 13 Jul 2006 20:33:09 +0000 Subject: [PATCH] Andy Armstrong provides new serial layer and refactors magellan to work with it. --- Makefile.in | 2 +- gbser.h | 120 ++++- gbser_posix.c | 440 +++++++++++++--- gbser_win.c | 515 ++++++++++++------ gpsutil.c | 4 +- jeeps/gpsserial.c | 2 +- magproto.c | 282 ++-------- msvc/GPSBabel.sln | 41 +- msvc/GPSBabel.vcproj | 22 +- reference/wbt-200.bin | Bin 0 -> 3120 bytes reference/wbt-200.gpx | 1170 +++++++++++++++++++++++++++++++++++++++++ testo | 8 + vecs.c | 11 +- wbt-200.c | 306 +++++------ 14 files changed, 2259 insertions(+), 664 deletions(-) create mode 100644 reference/wbt-200.bin create mode 100644 reference/wbt-200.gpx diff --git a/Makefile.in b/Makefile.in index b2489749e..16b938abe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,7 +73,7 @@ SHAPE=shapelib/shpopen.o shapelib/dbfopen.o LIBOBJS = queue.o route.o waypt.o filter_vecs.o util.o vecs.o mkshort.o \ csv_util.o strptime.o grtcirc.o vmem.o util_crc.o xmlgeneric.o \ uuid.o formspec.o xmltag.o cet.o cet_util.o fatal.o rgbcolors.o \ - inifile.o garmin_fs.o gbsleep.o units.o textfile.o @GBSER@ \ + inifile.o garmin_fs.o gbsleep.o units.o textfile.o @GBSER@ gbser.o \ $(COLDSYNC) $(GARMIN) $(JEEPS) $(SHAPE) $(FMTS) $(FILTERS) OBJS = main.o globals.o $(LIBOBJS) diff --git a/gbser.h b/gbser.h index 44cf3f787..550c5313d 100644 --- a/gbser.h +++ b/gbser.h @@ -19,11 +19,119 @@ */ -void *gbser_init(const char *name); -void gbser_deinit (void *); -int gbser_read(void *handle, char *ibuf, int sz); -int gbser_setspeed(void *handle, unsigned speed); +#ifndef __GBSER_H +#define __GBSER_H -#if __WIN32__ -char * fix_win_serial_name(const char *comname); +#define gbser_OK 0 +#define gbser_NOTHING -1 +#define gbser_TIMEOUT -2 +#define gbser_ERROR -3 + +#if defined(__WIN32__) || defined(__CYGWIN__) +#define WINSERIAL 1 +#else +#define POSIXSERIAL 1 #endif + +/* Open a serial port. |port_name| is the (platform specific) name + * of the serial device to open. Under WIN32 familiar DOS port names + * ('com1:') are translated into the equivalent name required by + * WIN32 + */ +void *gbser_init(const char *port_name); + +/* Close a serial port + */ +void gbser_deinit(void *handle); + +/* Set the serial port speed. + */ +int gbser_set_speed(void *handle, unsigned speed); + +/* Set the serial port speed, start, parity and stop bits */ +int gbser_set_port(void *handle, unsigned speed, + unsigned bits, + unsigned parity, + unsigned stop); + +/* Set the serial port up by parsing the supplied parameter string. + * Valid parameter strings look like '4800,8,N,1'. Parsing is case- + * insensitive, spaces are allowed around the commas and omitted + * trailing fields will default to '8', 'N' and '1' + */ +int gbser_setup(void *handle, const char *spec); + +/* Return true if there are characters available on the serial port + */ +int gbser_avail(void *handle); + +/* Read as many bytes as are available without blocking. At most |len| + * bytes will be read. Returns the number of bytes read or gbser_ERROR if an + * error occurs. + */ +int gbser_read(void *handle, void *buf, unsigned len); + +/* Read the specified number of bytes. Block until the requested number + * of bytes have been read or the timeout (in ms) is exceeded. + */ +int gbser_read_wait(void *handle, void *buf, unsigned len, unsigned ms); + +/* Read from the serial port until the specified |eol| character is + * found. Any character matching |discard| will be discarded. To + * read lines terminated by 0x0A0x0D discarding linefeeds use + * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A); + */ +int gbser_read_line(void *handle, void *buf, + unsigned len, unsigned ms, + int eol, int discard); + +/* Read a single character from the port, returning immediately if + * none are available. TODO: Define return values + */ +int gbser_readc(void *handle); + +/* Read a single character from the port, waiting up to |ms| + * milliseconds for a character to be available. + */ +int gbser_readc_wait(void *handle, unsigned ms); + +/* Discard any pending input on the serial port. + */ +int gbser_flush(void *handle); + +/* Write |len| bytes from |buf| to the serial port. + */ +int gbser_write(void *handle, const void *buf, unsigned len); + +/* Write a null terminated string in |str| to the serial + * port. + */ +int gbser_print(void *handle, const char *str); + +/* Write a single character to the serial port. + */ +int gbset_writec(void *handle, int c); + +/* Return true if a port name seems to refer to a serial port. + * On Windows this tests the filename (against the regex + * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of + * isatty() + */ +int gbser_is_serial(const char *port_name); + +/* This isn't part of the above abstraction; it's just a helper for + * the other serial modules in the tree. + * + * Windows does a weird thing with serial ports. + * COM ports 1 - 9 are "COM1:" through "COM9:" + * The one after that is \\.\\com10 - this function tries to plaster over + * that. + * It returns a pointer to a staticly allocated buffer and is therefore not + * thread safe. The buffer pointed to remains valid only until the next + * call to this function. + */ + +const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len); +const char *fix_win_serial_name(const char *comname); + +#endif /* GBSER_H */ diff --git a/gbser_posix.c b/gbser_posix.c index 0a54605dd..8a8186297 100644 --- a/gbser_posix.c +++ b/gbser_posix.c @@ -1,6 +1,6 @@ /* - Serial interface - POSIX layer. - + Serial interface for POSIX tty handling. + Copyright (C) 2006 Robert Lipe, robertlipe@usa.net This program is free software; you can redistribute it and/or modify @@ -21,129 +21,397 @@ #include "defs.h" #include "gbser.h" -#include +#include "gbser_private.h" + +#include #include #include #include +#include + +#include +#include -#define MYMAGIC 0x91827364 typedef struct { - struct termios orig_tio; - struct termios my_tio; - FILE *fh; - int fd; - unsigned long magic; -} gbser_posix_handle; - -static -speed_t -mkspeed(unsigned br) -{ - switch (br) { - case 1200: return B1200; - case 2400: return B2400; - case 4800: return B4800; - case 9600: return B9600; - case 19200: return B19200; + struct termios old_tio; + struct termios new_tio; + int fd; + unsigned vmin, vtime; + unsigned long magic; + + unsigned char inbuf[BUFSIZE]; + unsigned inbuf_used; +} gbser_handle; + +/* Wrapper to safely cast a void * into a gbser_handle */ +static gbser_handle *gbser__get_handle(void *p) { + gbser_handle *h = (gbser_handle *) p; + assert(h->magic == MYMAGIC); + return h; +} + +static speed_t mkspeed(unsigned br) { + switch (br) { + case 1200: return B1200; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; #if defined B57600 - case 57600: return B57600; + case 57600: return B57600; #endif #if defined B115200 - case 115200: return B115200; + case 115200: return B115200; #endif - default: return B4800; - } + default: + fatal("Unsupported serial speed: %d\n", br); + return 0; /* keep compiler happy */ + } } -/* -gbser_istty(void *handle) -{ - gbser_posix_handle *h = (gbser_posix_handle *) handle; - assert(h->magic == MYMAGIC); +typedef struct timeval hp_time; - return isatty(h->fd); +static void get_time(hp_time *tv) { + gettimeofday(tv, NULL); } -*/ -void * -gbser_init(const char *name) -{ - gbser_posix_handle *h; +static double elapsed(hp_time *tv) { + hp_time now; + double ot = (double) tv->tv_sec * 1000 + + (double) tv->tv_usec / 1000; + double nt; + gettimeofday(&now, NULL); + nt = (double) now.tv_sec * 1000 + + (double) now.tv_usec / 1000; + /*printf("elapsed -> %f\n", nt - ot);*/ + return nt - ot; +} + +static int set_rx_timeout(gbser_handle *h, unsigned vmin, unsigned vtime) { + if (vmin > 255) { vmin = 255; } + if (vtime > 255) { vtime = 255; } + if (vmin != h->vmin || vtime != h->vtime) { + h->vmin = h->new_tio.c_cc[VMIN] = vmin; + h->vtime = h->new_tio.c_cc[VTIME] = vtime; + + /*printf("VMIN=%d, VTIME=%d\n", h->vmin, h->vtime);*/ + + return tcsetattr(h->fd, TCSANOW, &h->new_tio) ? gbser_ERROR : gbser_OK; + } else { + return 0; + } +} + +/* Open a serial port. |port_name| is the (platform specific) name + * of the serial device to open. Under WIN32 familiar DOS port names + * ('com1:') are translated into the equivalent name required by + * WIN32 + */ +void *gbser_init(const char *port_name) { + gbser_handle *h; + + gbser__db(4, "gbser_init(\"%s\")\n", port_name); h = xcalloc(sizeof *h, 1); h->magic = MYMAGIC; + h->vmin = h->vtime = 0; + + if (h->fd = open(port_name, O_RDWR | O_NOCTTY), h->fd == -1) { + gbser__db(1, "Failed to open port (%s)\n", strerror(errno)); + goto failed; + } - h->fh = xfopen(name, "rb", "serial layer"); - h->fd = fileno(h->fh); -// h->fd = open(name, O_RDWR | O_EXCL | O_SYNC); - if (!isatty(h->fd)) { - goto open_fail; + gbser__db(1, "%s is not a TTY\n"); + goto failed; } - tcgetattr(h->fd, &h->orig_tio); - - h->my_tio = h->orig_tio; - h->my_tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR| - IGNCR|IGNCR|IXON); - h->my_tio.c_cflag &= ~(CSIZE|PARENB); - h->my_tio.c_cflag |= CS8; - h->my_tio.c_oflag = 0; - h->my_tio.c_lflag = 0; - h->my_tio.c_iflag = 0; - h->my_tio.c_cc[VTIME] = 10; /* Time out after one second */ - h->my_tio.c_cc[VMIN] = 255; + if (gbser_set_port(h, 4800, 8, 0, 1)) { + gbser__db(1, "gbser_set_port() failed\n"); + goto failed; + } return h; -open_fail: - if (h->fh) { - fclose(h->fh); +failed: + if (h->fd != -1) { + close(h->fd); } + xfree(h); + return NULL; } -void -gbser_deinit(void *handle) -{ - gbser_posix_handle *h = (gbser_posix_handle *) handle; - assert(h->magic == MYMAGIC); +/* Close a serial port + */ +void gbser_deinit(void *handle) { + gbser_handle *h = gbser__get_handle(handle); + + tcsetattr(h->fd, TCSAFLUSH, &h->old_tio); + close(h->fd); - tcsetattr(h->fd, TCSAFLUSH, &h->orig_tio); - fclose(h->fh); - xfree(handle); + xfree(h); } -int -gbser_setspeed(void *handle, unsigned speed) -{ +int gbser_set_port(void *handle, unsigned speed, unsigned bits, unsigned parity, unsigned stop) { + gbser_handle *h = gbser__get_handle(handle); speed_t s; - gbser_posix_handle *h = (gbser_posix_handle *) handle; - assert(h->magic == MYMAGIC); + + static unsigned bit_flags[] = { + 0, 0, 0, 0, 0, CS5, CS6, CS7, CS8 + }; + + if (bits < 5 || bits > 8) { + fatal("Unsupported bits setting: %d\n", bits); + } + + if (parity > 2) { + fatal("Unsupported parity setting: %d\n", parity); + } + + if (stop < 1 || stop > 2) { + fatal("Unsupported stop setting: %d\n", stop); + } s = mkspeed(speed); - cfsetospeed(&h->my_tio, s); - cfsetispeed(&h->my_tio, s); + /* TODO: We don't /fully/ initialise the port's stat here... */ + + tcgetattr(h->fd, &h->old_tio); + + h->new_tio = h->old_tio; + + /* clear bits */ + cfmakeraw(&h->new_tio); + + h->new_tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | + INLCR | IGNCR | IXON); + h->new_tio.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); + + /* set data bits, */ + h->new_tio.c_cflag |= bit_flags[bits]; + + /* stop bits and... */ + if (stop == 2) { + h->new_tio.c_cflag |= CSTOPB; + } + + /* parity */ + if (parity != 0) { + h->new_tio.c_cflag |= PARENB; + if (parity == 1) { + h->new_tio.c_cflag |= PARODD; + } + } + + h->new_tio.c_oflag = 0; + h->new_tio.c_lflag = 0; - return !tcsetattr(h->fd, TCSAFLUSH, &h->my_tio); + h->new_tio.c_cc[VMIN] = h->vmin; + h->new_tio.c_cc[VTIME] = h->vtime; + + cfsetospeed(&h->new_tio, s); + cfsetispeed(&h->new_tio, s); + + return tcsetattr(h->fd, TCSADRAIN, &h->new_tio) ? gbser_ERROR : gbser_OK; +} + +unsigned gbser__read_buffer(void *handle, void **buf, unsigned *len) { + gbser_handle *h = gbser__get_handle(handle); + unsigned count = *len; + unsigned char *cp = *buf; + if (count > h->inbuf_used) { + count = h->inbuf_used; + } + + memcpy(cp, h->inbuf, count); + memmove(h->inbuf, h->inbuf + count, + h->inbuf_used - count); + h->inbuf_used -= count; + *len -= count; + cp += count; + *buf = (void *) cp; + return count; } -int -gbser_read(void *handle, char *ibuf, int size) -{ - gbser_posix_handle *h = (gbser_posix_handle *) handle; +/* Return when the input buffer contains at least |want| bytes or |*ms| + * milliseconds have elapsed. |ms| may be NULL or |*ms| may be zero to + * poll the port for available bytes and return immediately. |*ms| will + * be updated to indicate the remaining time on exit. + * Returns the number of bytes available (>=0) or an error code (<0). + */ +int gbser__fill_buffer(void *handle, unsigned want, unsigned *ms) { + int rc; + gbser_handle *h = gbser__get_handle(handle); + + if (want > BUFSIZE) { + want = BUFSIZE; + } - ibuf[0] = 0; - assert(h->magic == MYMAGIC); + /* Already got enough bytes? */ + if (h->inbuf_used >= want) { + return h->inbuf_used; + } + + if (NULL == ms || 0 == *ms) { + if ((rc = set_rx_timeout(h, 0, 0), rc < 0) || + (rc = read(h->fd, h->inbuf + h->inbuf_used, + want - h->inbuf_used), rc < 0)) { + return gbser_ERROR; + } + h->inbuf_used += rc; + /*printf("Got %d bytes\n", rc);*/ + } else { + hp_time tv; + get_time(&tv); + double time_left = *ms; + + for (;;) { + fd_set rec; + struct timeval t; + + time_left = *ms - elapsed(&tv); + if (time_left <= 0 || h->inbuf_used >= want) { + break; + } + + FD_ZERO(&rec); + FD_SET(h->fd, &rec); + + t.tv_sec = (time_t) time_left / 1000; + t.tv_usec = (suseconds_t) ((unsigned) time_left % 1000) * 1000; + + if (select(h->fd + 1, &rec, NULL, NULL, &t) < 0) { + return gbser_ERROR; + } + + time_left = *ms - elapsed(&tv); + + if (FD_ISSET(h->fd, &rec)) { + unsigned vmin = 0, vtime = 0; + if (time_left >= 100) { + vmin = want - h->inbuf_used; + vtime = (unsigned) time_left / 100; + } + if ((rc = set_rx_timeout(h, vmin, vtime), rc < 0) || + (rc = read(h->fd, h->inbuf + h->inbuf_used, + want - h->inbuf_used), rc < 0)) { + return gbser_ERROR; + } + h->inbuf_used += rc; + /*printf("Got %d bytes\n", rc);*/ + } + } + *ms = (time_left < 0) ? 0 : time_left; + } + + return h->inbuf_used; +} + +/* Discard any pending input on the serial port. + */ +int gbser_flush(void *handle) { + gbser_handle *h = gbser__get_handle(handle); + h->inbuf_used = 0; + if (tcflush(h->fd, TCIFLUSH)) { + return gbser_ERROR; + } + + return gbser_OK; +} + +/* Write |len| bytes from |buf| to the serial port. + */ +int gbser_write(void *handle, const void *buf, unsigned len) { + gbser_handle *h = gbser__get_handle(handle); + const char *bp = buf; + int rc; + while (len > 0) { + /*printf("write(%d, %p, %d)\n", h->fd, bp, len);*/ + if (rc = write(h->fd, bp, len), rc < 0) { + printf("rc = %d, errno = %d (%s)\n", rc, errno, strerror(errno)); + return gbser_ERROR; + } + len -= rc; + bp += rc; + } + return gbser_OK; +} + +/* Return true if a port name seems to refer to a serial port. + * On Windows this tests the filename (against the regex + * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of + * isatty() + */ + +int gbser_is_serial(const char *port_name) { + int fd; + int is_port = 0; + + if (fd = open(port_name, O_RDWR | O_NOCTTY), fd == -1) { + gbser__db(1, "Failed to open port (%s) to check its type\n", strerror(errno)); + return 0; + } -// n = read(h->fd, ibuf, size); -// printf("Returning %d\n", n); -redo: - fgets(ibuf, size, h->fh); -// rtrim(ibuf); -if (strlen(ibuf) == 0) - goto redo; - return 1; + is_port = isatty(fd); + + close(fd); + + return is_port; +} + +/* This isn't part of the above abstraction; it's just a helper for + * the other serial modules in the tree. + * + * Windows does a weird thing with serial ports. + * COM ports 1 - 9 are "COM1:" through "COM9:" + * The one after that is \\.\\com10 - this function tries to plaster over + * that. + * It returns a pointer to a staticly allocated buffer and is therefore not + * thread safe. The buffer pointed to remains valid only until the next + * call to this function. + */ + +const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len) { + strncpy(obuf, comname, len); + return obuf; +} + +static char gb_com_buffer[100]; + +const char *fix_win_serial_name(const char *comname) { + return fix_win_serial_name_r(comname, gb_com_buffer, sizeof(gb_com_buffer)); +} + +/* Read from the serial port until the specified |eol| character is + * found. Any character matching |discard| will be discarded. To + * read lines terminated by 0x0A0x0D discarding linefeeds use + * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A); + */ +int gbser_read_line(void *handle, void *buf, + unsigned len, unsigned ms, + int eol, int discard) { + char *bp = buf; + unsigned pos = 0; + hp_time tv; + get_time(&tv); + bp[pos] = '\0'; + for (;;) { + unsigned time_left = ms - elapsed(&tv); + int c; + if (time_left <= 0) { + return gbser_TIMEOUT; + } + c = gbser_readc_wait(handle, time_left); + if (c == gbser_ERROR) { + return c; + } else if (c == eol) { + return gbser_OK; + } + if (c != gbser_NOTHING && c != discard && pos < len - 1) { + bp[pos++] = c; + bp[pos] = '\0'; + } + } } diff --git a/gbser_win.c b/gbser_win.c index c113614ff..a339cf86a 100644 --- a/gbser_win.c +++ b/gbser_win.c @@ -1,6 +1,6 @@ /* Serial interface - Windows layer. - + Copyright (C) 2006 Robert Lipe, robertlipe@usa.net This program is free software; you can redistribute it and/or modify @@ -21,168 +21,90 @@ #include "defs.h" #include "gbser.h" +#include "gbser_private.h" + #include #include -typedef struct gbser_win_handle { - HANDLE comport; -} gbser_win_handle; +#include +#include -#define xCloseHandle(a) if (a) { CloseHandle(a); } a = NULL; +typedef struct { + HANDLE comport; + DWORD timeout; + unsigned long magic; -void * -gbser_init(const char *portname) -{ -// DCB tio; - COMMTIMEOUTS timeout; - HANDLE comport; -// char *xname= xstrdup("\\\\.\\\\"); - char *xname = fix_win_serial_name(portname); - gbser_win_handle* handle = xcalloc(1, sizeof(*handle));; - -// /* Amazingly, windows will fail the open below unless we -// * prepend \\.\ to the name. It also then fails the open -// * unless we strip the colon from the name. Aaaaargh! -// */ -// xname = xstrappend(xname, portname); -// if (xname[strlen(xname)-1] == ':') -// xname[strlen(xname)-1] = 0; -// xCloseHandle(comport); - - comport = CreateFile(xname, GENERIC_READ|GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, 0, NULL); + unsigned char inbuf[BUFSIZE]; + unsigned inbuf_used; +} gbser_handle; - if (comport == INVALID_HANDLE_VALUE) { - return NULL; - } - handle->comport = comport; +#define DEV_PREFIX "\\\\.\\\\" -#if 0 - tio.DCBlength = sizeof(DCB); - GetCommState (comport, &tio); -// tio.BaudRate = mkspeed(bitrate); - { - extern int mkspeed(int); - tio.BaudRate = mkspeed(4800); - } - tio.fBinary = TRUE; - tio.fParity = TRUE; - tio.fOutxCtsFlow = FALSE; - tio.fOutxDsrFlow = FALSE; - tio.fDtrControl = DTR_CONTROL_ENABLE; - tio.fDsrSensitivity = FALSE; - tio.fTXContinueOnXoff = TRUE; - tio.fOutX = FALSE; - tio.fInX = FALSE; - tio.fErrorChar = FALSE; - tio.fNull = FALSE; - tio.fRtsControl = RTS_CONTROL_ENABLE; - tio.fAbortOnError = FALSE; - tio.ByteSize = 8; - tio.Parity = NOPARITY; - tio.StopBits = ONESTOPBIT; - - if (!SetCommState (comport, &tio)) { - /* - * Probably not a com port. Let caller try it as a file. - */ - - return NULL; - } -#else - gbser_setspeed(handle, 4800); -#endif - - GetCommTimeouts (comport, &timeout); - /* We basically do single character reads and simulate line input - * mode, so these values are kind of fictional. - */ - timeout.ReadIntervalTimeout = 1000; - timeout.ReadTotalTimeoutMultiplier = 1000; - timeout.ReadTotalTimeoutConstant = 1000; - timeout.WriteTotalTimeoutMultiplier = 1000; - timeout.WriteTotalTimeoutConstant = 1000; - if (!SetCommTimeouts (comport, &timeout)) { - xCloseHandle (comport); - fatal("SetCommTimeouts failed.\n"); - } - return handle; +/* Wrapper to safely cast a void * into a gbser_handle */ +static gbser_handle *gbser__get_handle(void *p) { + gbser_handle *h = (gbser_handle *) p; + assert(h->magic == MYMAGIC); + return h; } -/* - * Returns 1 on success, 0 on errro. - */ -int -gbser_setspeed(void *handle, unsigned speed) -{ - extern int mkspeed(int); /* From magproto.c */ - gbser_win_handle *h = (gbser_win_handle *) handle; - DCB tio; - - tio.DCBlength = sizeof(DCB); - GetCommState(h->comport, &tio); - - tio.BaudRate = mkspeed(speed); - tio.fBinary = TRUE; - tio.fParity = TRUE; - tio.fOutxCtsFlow = FALSE; - tio.fOutxDsrFlow = FALSE; - tio.fDtrControl = DTR_CONTROL_ENABLE; - tio.fDsrSensitivity = FALSE; - tio.fTXContinueOnXoff = TRUE; - tio.fOutX = FALSE; - tio.fInX = FALSE; - tio.fErrorChar = FALSE; - tio.fNull = FALSE; - tio.fRtsControl = RTS_CONTROL_ENABLE; - tio.fAbortOnError = FALSE; - tio.ByteSize = 8; - tio.Parity = NOPARITY; - tio.StopBits = ONESTOPBIT; - - if (!SetCommState (h->comport, &tio)) { - /* - * Probably not a com port. Let caller try it as a file. - */ - - return 0; +static DWORD mkspeed(unsigned br) { + switch (br) { + case 1200: return CBR_1200; + case 2400: return CBR_2400; + case 4800: return CBR_4800; + case 9600: return CBR_9600; + case 19200: return CBR_19200; + case 57600: return CBR_57600; + case 115200: return CBR_115200; + default: + fatal("Unsupported serial speed: %d\n", br); + return 0; /* keep compiler happy */ } - return 1; } -int -gbser_read(void *handle, char *ibuf, int size) -{ - gbser_win_handle *h = (gbser_win_handle *) handle; - int i = 0; - DWORD cnt; - - ibuf[0]='0'; - for(;i < size;i++) { - if (ReadFile (h->comport, &ibuf[i], 1, &cnt, NULL) != TRUE) - break; - if (cnt < 1) - return 0; - if (ibuf[i] == '\n') - break; - } +typedef LARGE_INTEGER hp_time; - ibuf[i] = 0; - return 1; +static void get_time(hp_time *tv) { + QueryPerformanceCounter(tv); } -void -gbser_deinit(void *handle) -{ - gbser_win_handle *h = (gbser_win_handle *) handle; - xfree(h); -} +static double elapsed(hp_time *tv) { + hp_time now; + LARGE_INTEGER tps; + QueryPerformanceFrequency(&tps); + QueryPerformanceCounter(&now); + return ((double) (now.QuadPart - tv->QuadPart) / + (double) tps.QuadPart) * 1000; +} +static int set_rx_timeout(gbser_handle *h, DWORD timeout) { + if (timeout != h->timeout) { + COMMTIMEOUTS to; -/* - * This isn't part of the above abstraction; it's just a helper for + if (!GetCommTimeouts(h->comport, &to)) { + return gbser_ERROR; + } + + to.ReadIntervalTimeout = timeout; + to.ReadTotalTimeoutMultiplier = 0; + to.ReadTotalTimeoutConstant = timeout; + to.WriteTotalTimeoutMultiplier = 0; + to.WriteTotalTimeoutConstant = 0; + + if (!SetCommTimeouts(h->comport, &to)) { + return gbser_ERROR; + } else { + h->timeout = timeout; + return gbser_OK; + } + } else { + return gbser_OK; + } +} + +/* This isn't part of the above abstraction; it's just a helper for * the other serial modules in the tree. * * Windows does a weird thing with serial ports. @@ -193,20 +115,301 @@ gbser_deinit(void *handle) * thread safe. The buffer pointed to remains valid only until the next * call to this function. */ -static char gb_com_buffer[100]; -char * -fix_win_serial_name(const char *comname) -{ - /* If in the form 'COMx:', use it in place */ - if ((strlen(comname) == 5) && (comname[4] == ':')) { - strcpy(gb_com_buffer, comname); +const char *fix_win_serial_name_r(const char *comname, char *obuf, size_t len) { + if (!gbser_is_serial(comname) || + ((strlen(comname) == 5) && (comname[4] == ':'))) { + strncpy(obuf, comname, len); } else { - snprintf(gb_com_buffer, sizeof(gb_com_buffer), - "\\\\.\\\\%s", comname); - if (gb_com_buffer[strlen(gb_com_buffer) - 1 ] == ':') { - gb_com_buffer[strlen(gb_com_buffer) - 1] = 0; + size_t l; + snprintf(obuf, len, DEV_PREFIX "%s", comname); + l = strlen(obuf); + if (obuf[l - 1] == ':') { + obuf[l - 1] = '\0'; } } - return gb_com_buffer; + + return obuf; +} + +static char gb_com_buffer[100]; + +const char *fix_win_serial_name(const char *comname) { + return fix_win_serial_name_r(comname, gb_com_buffer, sizeof(gb_com_buffer)); +} + +/* Open a serial port. |port_name| is the (platform specific) name + * of the serial device to open. Under WIN32 familiar DOS port names + * ('com1:') are translated into the equivalent name required by + * WIN32 + */ +void *gbser_init(const char *port_name) { + HANDLE comport; + gbser_handle* h = xcalloc(1, sizeof(*h)); + const char *xname = fix_win_serial_name(port_name); + + gbser__db(2, "Translated port name: \"%s\"\n", xname); + + h->magic = MYMAGIC; + + comport = CreateFile(xname, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + + if (comport == INVALID_HANDLE_VALUE) { + goto failed; + } + + h->comport = comport; + h->timeout = 1; + + if (gbser_set_port(h, 4800, 8, 0, 1) || set_rx_timeout(h, 0)) { + goto failed; + } + + return h; + +failed: + if (comport) { CloseHandle(h->comport); } + xfree(h); + + return NULL; +} + +/* Close a serial port + */ +void gbser_deinit(void *handle) { + gbser_handle *h = gbser__get_handle(handle); + + CloseHandle(h->comport); + + xfree(h); +} + +int gbser_set_port(void *handle, unsigned speed, unsigned bits, unsigned parity, unsigned stop) { + gbser_handle *h = gbser__get_handle(handle); + DCB tio; + + if (bits < 5 || bits > 8) { + fatal("Unsupported bits setting: %d\n", bits); + } + + if (parity > 2) { + fatal("Unsupported parity setting: %d\n", parity); + } + + if (stop < 1 || stop > 2) { + fatal("Unsupported stop setting: %d\n", stop); + } + + tio.DCBlength = sizeof(DCB); + GetCommState(h->comport, &tio); + + tio.BaudRate = mkspeed(speed); + tio.fBinary = TRUE; + tio.fParity = TRUE; + tio.fOutxCtsFlow = FALSE; + tio.fOutxDsrFlow = FALSE; + tio.fDtrControl = DTR_CONTROL_ENABLE; + tio.fDsrSensitivity = FALSE; + tio.fTXContinueOnXoff = TRUE; + tio.fOutX = FALSE; + tio.fInX = FALSE; + tio.fErrorChar = FALSE; + tio.fNull = FALSE; + tio.fRtsControl = RTS_CONTROL_ENABLE; + tio.fAbortOnError = FALSE; + tio.ByteSize = bits; + tio.Parity = parity == 0 ? NOPARITY : + (parity == 1 ? ODDPARITY : EVENPARITY); + tio.StopBits = stop == 1 ? ONESTOPBIT : TWOSTOPBITS; + + if (!SetCommState(h->comport, &tio)) { + return gbser_ERROR; + } + return gbser_OK; +} + +unsigned gbser__read_buffer(void *handle, void **buf, unsigned *len) { + gbser_handle *h = gbser__get_handle(handle); + unsigned count = *len; + unsigned char *cp = *buf; + if (count > h->inbuf_used) { + count = h->inbuf_used; + } + + memcpy(cp, h->inbuf, count); + memmove(h->inbuf, h->inbuf + count, + h->inbuf_used - count); + h->inbuf_used -= count; + *len -= count; + cp += count; + *buf = (void *) cp; + return count; +} + +/* Return when the input buffer contains at least |want| bytes or |*ms| + * milliseconds have elapsed. |ms| may be NULL or |*ms| may be zero to + * poll the port for available bytes and return immediately. |*ms| will + * be updated to indicate the remaining time on exit. + * Returns the number of bytes available (>=0) or an error code (<0). + */ +int gbser__fill_buffer(void *handle, unsigned want, unsigned *ms) { + int rc; + gbser_handle *h = gbser__get_handle(handle); + + if (want > BUFSIZE) { + want = BUFSIZE; + } + + /* Already got enough bytes? */ + if (h->inbuf_used >= want) { + return h->inbuf_used; + } + + if (NULL == ms || 0 == *ms) { + DWORD err, nread; + COMSTAT stat; + ClearCommError(h->comport, &err, &stat); + if (stat.cbInQue > 0) { + DWORD count = want - h->inbuf_used; + if (count > stat.cbInQue) { + count = stat.cbInQue; + } + if (rc = set_rx_timeout(h, 1), rc) { + return rc; + } + if (!ReadFile(h->comport, h->inbuf + h->inbuf_used, + count, &nread, NULL)) { + err = GetLastError(); + if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) { + return gbser_ERROR; + } + } + h->inbuf_used += nread; + } + } else { + hp_time tv; + double time_left; + DWORD err, nread; + get_time(&tv); + if (rc = set_rx_timeout(h, *ms), rc) { + return rc; + } + if (!ReadFile(h->comport, h->inbuf + h->inbuf_used, + want - h->inbuf_used, + &nread, NULL)) { + err = GetLastError(); + if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) { + return gbser_ERROR; + } + } + h->inbuf_used += nread; + time_left = *ms - elapsed(&tv); + *ms = time_left < 0 ? 0 : (unsigned) time_left; + } + + return h->inbuf_used; +} + +/* Discard any pending input on the serial port. + */ +int gbser_flush(void *handle) { + gbser_handle *h = gbser__get_handle(handle); + h->inbuf_used = 0; + if (!PurgeComm(h->comport, PURGE_RXCLEAR)) { + return gbser_ERROR; + } + return gbser_OK; +} + +/* Write |len| bytes from |buf| to the serial port. + */ +int gbser_write(void *handle, const void *buf, unsigned len) { + gbser_handle *h = gbser__get_handle(handle); + DWORD nwritten; + const char *bp = buf; + /* Not sure we need to spin here - but this'll work even if we don't */ + while (len > 0) { + if (!WriteFile(h->comport, bp, len, &nwritten, NULL)) { + return gbser_ERROR; + } + len -= nwritten; + bp += nwritten; + } + return gbser_OK; +} + +/* Return true if a port name seems to refer to a serial port. + * On Windows this tests the filename (against the regex + * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of + * isatty() + */ + +int gbser_is_serial(const char *port_name) { + const char *pfx = DEV_PREFIX; + size_t pfx_l = strlen(pfx); + const char *com = "COM"; + size_t com_l = strlen(com); + unsigned digits; + + /* Skip any prefix */ + if (memcmp(port_name, pfx, pfx_l) == 0) { + port_name += pfx_l; + } + + if (case_ignore_strncmp(port_name, com, com_l) != 0) { + return 0; + } + + port_name += com_l; + for (digits = 0; isdigit(*port_name); port_name++, digits++) { + /* do nothing */ + } + + if (digits == 0) { + return 0; + } + + if (*port_name == ':') { + port_name++; + } + + if (*port_name != '\0') { + return 0; + } + + /* Success! */ + return 1; +} + +/* Read from the serial port until the specified |eol| character is + * found. Any character matching |discard| will be discarded. To + * read lines terminated by 0x0A0x0D discarding linefeeds use + * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A); + */ +int gbser_read_line(void *handle, void *buf, + unsigned len, unsigned ms, + int eol, int discard) { + char *bp = buf; + unsigned pos = 0; + hp_time tv; + get_time(&tv); + bp[pos] = '\0'; + for (;;) { + unsigned time_left = ms - elapsed(&tv); + int c; + if (time_left <= 0) { + return gbser_TIMEOUT; + } + c = gbser_readc_wait(handle, time_left); + if (c == gbser_ERROR) { + return c; + } else if (c == eol) { + return gbser_OK; + } + if (c != gbser_NOTHING && c != discard && pos < len - 1) { + bp[pos++] = c; + bp[pos] = '\0'; + } + } } diff --git a/gpsutil.c b/gpsutil.c index 9881b7d9b..768fc5e6f 100644 --- a/gpsutil.c +++ b/gpsutil.c @@ -66,13 +66,13 @@ data_read(void) char icon[3] = {0}; waypoint *wpt_tmp; textfile_t *tin; - - tin = textfile_init(file_in); /* * Make sure that all waypoints in single read have same * timestamp. */ time_t now = current_time(); + + tin = textfile_init(file_in); while ((ibuf = textfile_read(tin))) { /* A sharp in column zero or an blank line is a comment */ diff --git a/jeeps/gpsserial.c b/jeeps/gpsserial.c index f714cb997..1abc30669 100644 --- a/jeeps/gpsserial.c +++ b/jeeps/gpsserial.c @@ -89,7 +89,7 @@ int32 GPS_Serial_On(const char *port, gpsdevh **dh) DCB tio; COMMTIMEOUTS timeout; HANDLE comport; - char *xname = fix_win_serial_name(port); + const char *xname = fix_win_serial_name(port); win_serial_data *wsd = xcalloc(sizeof (win_serial_data), 1); *dh = (gpsdevh*) wsd; diff --git a/magproto.c b/magproto.c index 18fd72115..03bea8c1e 100644 --- a/magproto.c +++ b/magproto.c @@ -35,7 +35,7 @@ static int explorist; #define debug_serial (global_opts.debug_level > 1) -static char * termread(char *ibuf, int size); +static char *termread(char *ibuf, int size); static void termwrite(char *obuf, int size); static void mag_readmsg(gpsdata_type objective); static void mag_handon(void); @@ -77,9 +77,7 @@ typedef struct mag_rte_head { static queue rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */ -static FILE *magfile_in; -static FILE *magfile_out; -static int magfd; +static FILE *magfile_h; static mag_rxstate magrxstate; static int mag_error; static unsigned int last_rx_csum; @@ -507,7 +505,7 @@ retry: ignore_unable = 0; return; } - if (IS_TKN("$PMGNCMD,END") || (is_file && (feof(magfile_in)))) { + if (IS_TKN("$PMGNCMD,END") || (is_file && (feof(magfile_h)))) { found_done = 1; return; } @@ -515,226 +513,65 @@ retry: mag_writeack(isum); } -/* - * termio on Cygwin is apparently broken, so we revert to Windows serial. - */ -#if defined (__WIN32__) || defined (__CYGWIN__) - -#include - -DWORD -mkspeed(bitrate) -{ - switch (bitrate) { - case 1200: return CBR_1200; - case 2400: return CBR_2400; - case 4800: return CBR_4800; - case 9600: return CBR_9600; - case 19200: return CBR_19200; - case 57600: return CBR_57600; - case 115200: return CBR_115200; - default: return CBR_4800; - } -} - -HANDLE comport = NULL; - -#define xCloseHandle(a) if (a) { CloseHandle(a); } a = NULL; - -static -int -terminit(const char *portname, int create_ok) -{ - DCB tio; - char *xname = fix_win_serial_name(portname); - COMMTIMEOUTS timeout; - - is_file = 0; +static void *serial_handle = NULL; - xCloseHandle(comport); - - comport = CreateFile(xname, GENERIC_READ|GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, 0, NULL); - if (comport == INVALID_HANDLE_VALUE) { - goto try_as_file; - } - tio.DCBlength = sizeof(DCB); - GetCommState (comport, &tio); - tio.BaudRate = mkspeed(bitrate); - tio.fBinary = TRUE; - tio.fParity = TRUE; - tio.fOutxCtsFlow = FALSE; - tio.fOutxDsrFlow = FALSE; - tio.fDtrControl = DTR_CONTROL_ENABLE; - tio.fDsrSensitivity = FALSE; - tio.fTXContinueOnXoff = TRUE; - tio.fOutX = FALSE; - tio.fInX = FALSE; - tio.fErrorChar = FALSE; - tio.fNull = FALSE; - tio.fRtsControl = RTS_CONTROL_ENABLE; - tio.fAbortOnError = FALSE; - tio.ByteSize = 8; - tio.Parity = NOPARITY; - tio.StopBits = ONESTOPBIT; - - if (!SetCommState (comport, &tio)) { - xCloseHandle(comport); - - /* - * Probably not a com port. Try it as a file. - */ -try_as_file: - magfile_in = xfopen(portname, create_ok ? "w+b" : "rb", MYNAME); +static int terminit(const char *portname, int create_ok) { + if (gbser_is_serial(portname)) { + if (serial_handle = gbser_init(portname), NULL != serial_handle) { + int rc; + if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) { + fatal(MYNAME ": Can't configure port\n"); + } + } + is_file = 0; + return 1; + } else { + /* Does this check for an error? */ + magfile_h = xfopen(portname, create_ok ? "w+b" : "rb", MYNAME); is_file = 1; icon_mapping = map330_icon_table; mag_cleanse = m330_cleanse; got_version = 1; return 0; } - - GetCommTimeouts (comport, &timeout); - /* We basically do single character reads and simulate line input - * mode, so these values are kind of fictional. - */ - timeout.ReadIntervalTimeout = 1000; - timeout.ReadTotalTimeoutMultiplier = 1000; - timeout.ReadTotalTimeoutConstant = 1000; - timeout.WriteTotalTimeoutMultiplier = 1000; - timeout.WriteTotalTimeoutConstant = 1000; - if (!SetCommTimeouts (comport, &timeout)) { - xCloseHandle (comport); - fatal(MYNAME ": set timeouts\n"); - } - return 1; } -static char * -termread(char *ibuf, int size) -{ - int i=0; - DWORD cnt; - +static char *termread(char *ibuf, int size) { if (is_file) { - return fgets(ibuf, size, magfile_in); - } - - ibuf[i]='a'; - for(;i < size;i++) { - if (ReadFile (comport, &ibuf[i], 1, &cnt, NULL) != TRUE) - break; - if (cnt < 1) - return NULL; - if (ibuf[i] == '\n') - break; + return fgets(ibuf, size, magfile_h); + } else { + int rc = gbser_read_line(serial_handle, ibuf, size, 2000, '\x0a', '\x0d'); + if (rc != gbser_OK) { + fatal(MYNAME ": Read error\n"); + } + return ibuf; } - ibuf[i] = 0; - return ibuf; } -static void -termwrite(char *obuf, int size) -{ - DWORD len; - +static void termwrite(char *obuf, int size) { if (is_file) { - fwrite(obuf, size, 1, magfile_out); - return; - } - WriteFile (comport, obuf, size, &len, NULL); - if ((int) len != size) { - fatal(MYNAME ":. Wrote %d of %d bytes.\n", len, size); - } -} - -static -void -termdeinit() -{ - xCloseHandle(comport); -} - -#else - -#include -#include - -speed_t -mkspeed(unsigned br) -{ - switch (br) { - case 1200: return B1200; - case 2400: return B2400; - case 4800: return B4800; - case 9600: return B9600; - case 19200: return B19200; -#if defined B57600 - case 57600: return B57600; -#endif -#if defined B115200 - case 115200: return B115200; -#endif - default: return B4800; + size_t nw; + if (nw = fwrite(obuf, 1, size, magfile_h), nw < (size_t) size) { + fatal(MYNAME ": Write error"); + } + } else { + int rc; + if (rc = gbser_write(serial_handle, obuf, size), rc < 0) { + fatal(MYNAME ": Write error"); + } } } - -static struct termios orig_tio; -static void -terminit(const char *portname, int create_ok) -{ - struct termios new_tio; - - magfile_in = xfopen(portname, "rb", MYNAME); - - is_file = (0 == strcmp(portname,"-")) || !isatty(fileno(magfile_in)) || explorist; +static void termdeinit() { if (is_file) { - icon_mapping = map330_icon_table; - mag_cleanse = m330_cleanse; - got_version = 1; - return; - } - - magfile_out = xfopen(portname, "w+b", MYNAME); - magfd = fileno(magfile_in); - - tcgetattr(magfd, &orig_tio); - new_tio = orig_tio; - new_tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR| - IGNCR|ICRNL|IXON); - new_tio.c_oflag &= ~OPOST; - new_tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); - new_tio.c_cflag &= ~(CSIZE|PARENB); - new_tio.c_cflag |= CS8; - new_tio.c_cc[VTIME] = 10; - new_tio.c_cc[VMIN] = 0; - - cfsetospeed(&new_tio, mkspeed(bitrate)); - cfsetispeed(&new_tio, mkspeed(bitrate)); - tcsetattr(magfd, TCSAFLUSH, &new_tio); -} - -static void -termdeinit() -{ - if (!is_file) { - tcsetattr(magfd, TCSANOW, &orig_tio); + fclose(magfile_h); + magfile_h = NULL; + } else { + gbser_deinit(serial_handle); + serial_handle = NULL; } } -static char * -termread(char *ibuf, int size) -{ - return fgets(ibuf, size, magfile_in); -} - -static void -termwrite(char *obuf, int size) -{ - fwrite(obuf, size, 1, magfile_out); -} -#endif - /* * Arg tables are doubled up so that -? can output appropriate help */ @@ -861,38 +698,12 @@ mag_wr_init_common(const char *portname) wptcmtcnt_max = MAXCMTCT ; } -#if __WIN32__ - if (!terminit(portname, 1)) { - is_file = 1; - } -#else - magfile_out = xfopen(portname, "w+b", MYNAME); - is_file = (0 == strcmp(portname,"-")) || !isatty(fileno(magfile_out)) || explorist; -#endif + terminit(portname, 1); if (!mkshort_handle) { mkshort_handle = mkshort_new_handle(); } - if (is_file) { - magfile_out = xfopen(portname, "w+b", MYNAME); - icon_mapping = map330_icon_table; - mag_cleanse = m330_cleanse; - got_version = 1; - } else { - /* - * This is a serial device. The line has to be open for - * reading and writing, so we let rd_init do the dirty work. - */ - if (magfile_out) { - fclose(magfile_out); - } -#if __WIN32__ - if (comport) { - xCloseHandle(comport); - } -#endif - mag_rd_init(portname); - } + QUEUE_INIT(&rte_wpt_tmp); } @@ -928,9 +739,6 @@ mag_deinit(void) { mag_handoff(); termdeinit(); - if(magfile_in) - fclose(magfile_in); - magfile_in = NULL; if(mkshort_handle) mkshort_del_handle(&mkshort_handle); @@ -1240,8 +1048,8 @@ static void mag_read(void) { found_done = 0; - if (global_opts.masked_objective & TRKDATAMASK) { + magrxstate = mrs_handoff; if (!is_file) mag_writemsg("PMGNCMD,TRACK,2"); @@ -1251,6 +1059,7 @@ mag_read(void) } if (global_opts.masked_objective & WPTDATAMASK) { + magrxstate = mrs_handoff; if (!is_file) mag_writemsg("PMGNCMD,WAYPOINT"); @@ -1260,6 +1069,7 @@ mag_read(void) } if (global_opts.masked_objective & RTEDATAMASK) { + magrxstate = mrs_handoff; if (!is_file) { /* * serial routes require waypoint & routes diff --git a/msvc/GPSBabel.sln b/msvc/GPSBabel.sln index 3748f751f..538236d6f 100644 --- a/msvc/GPSBabel.sln +++ b/msvc/GPSBabel.sln @@ -1,21 +1,20 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPSBabel", "GPSBabel.vcproj", "{EB2609DB-5800-45B2-BCB2-06D72F389DCA}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Debug.ActiveCfg = Debug|Win32 - {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Debug.Build.0 = Debug|Win32 - {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Release.ActiveCfg = Release|Win32 - {EB2609DB-5800-45B2-BCB2-06D72F389DCA}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GPSBabel", "GPSBabel.vcproj", "{D81B30A1-C560-42BC-AE80-257FDDD484D8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Debug|Win32.Build.0 = Debug|Win32 + {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Release|Win32.ActiveCfg = Release|Win32 + {D81B30A1-C560-42BC-AE80-257FDDD484D8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc/GPSBabel.vcproj b/msvc/GPSBabel.vcproj index 2ec9b9b65..8fc9f48b1 100644 --- a/msvc/GPSBabel.vcproj +++ b/msvc/GPSBabel.vcproj @@ -3,7 +3,7 @@ ProjectType="Visual C++" Version="8.00" Name="GPSBabel" - ProjectGUID="{EB2609DB-5800-45B2-BCB2-06D72F389DCA}" + ProjectGUID="{D81B30A1-C560-42BC-AE80-257FDDD484D8}" > + + @@ -1543,6 +1547,10 @@ /> + + @@ -2250,6 +2258,14 @@ RelativePath="..\garmin_tables.h" > + + + + diff --git a/reference/wbt-200.bin b/reference/wbt-200.bin new file mode 100644 index 0000000000000000000000000000000000000000..a99d163971af83c4c1f8ab3e425b907c86f18749 GIT binary patch literal 3120 zcmXZeca&De83*9FQBZ>oIJq%~Yq>I?WxOU=;o97ht8p{Fi>q@t?#Z>d3t!EFoAc?;Nt(^s zypwadDzD}SeAIs}w%9~}F$?6lH@rAwUn53(? z+twIPwolUaeE+r>#L2sc~d4g8TR6z<@v6h`|{CEo>9+t@m+l2<|G}IvHX|t5Wd2Ac5af6)i=TiQ9Sq!tT zS3f-3{8!3$6_oKiT8dSLw%WVh=SOZx(o@`SWeh*_*_W#s9%CQRcTtz?{w;SiQzN9y@m%D#ZbYG z_;-adB4lGn=Tah}W#R>iPZ&INp$cgc1puaMgic^2^{Yt*grIoy@U8lTVO z_&Q#|udG!A<|*c6oo~>134h4Ft*4Ynt&gFqep1GN;V!(AhjBe#$4~IRypiYdJl?|Z za2apo4|qP8^A`SsckwCS%lo+ch8T|XL9WlW?QaG5`P8>5s}nqc7jjn6WBCYI<~#T& zu5xC)pE{|=gNWB9@ z6JLKw{cy=)z2dLxhhIMx!*8a!^YGjQG2ET6ez?qeH)ELk;pJOmSk+Jc@RPo&mv2=+ z{4n>{#|Cp_y*JgPMsORwrKNLlG?(i0i}d!f+(`d9?kvdXjrzk78BXGbU&XMvkNV+` z_U;nzRlrmAlNa=gncTsD4KZgC=W?F!aSpe3&R*MF{cy>bYUU30!%uLPKdT?U$ukYP zgdgL?noBd!^E58wdv@BFLF$LcyBp*TS3lfLCNGXxKm5^g_4$AyY26W zO#k5@?dke^)DJJK@Q%;9^Kg#4$D!Mjv?k}tYNa!-4qxX!@RnY86`v!kbGRuVH|J7y z(voxCHy&_jXvh1%(Ze5fpW)iuoI&cRE9at=O2k-!XruApBL}b nKgT8MGu*-d7CT3#@*1^Q=}~t-&fI^p)J*Ob?iKdB(E0U0T7=qB literal 0 HcmV?d00001 diff --git a/reference/wbt-200.gpx b/reference/wbt-200.gpx new file mode 100644 index 000000000..af01949c9 --- /dev/null +++ b/reference/wbt-200.gpx @@ -0,0 +1,1170 @@ + + + + + + + 0.556136 + WP0001 + + + 94.133018 + 0.247108 + WP0002 + + + 58.742805 + 0.026882 + WP0003 + + + 0.085008 + WP0004 + + + 0.065847 + WP0005 + + + 18.051563 + 0.050291 + WP0006 + + + 96.898911 + 0.053764 + WP0007 + + + 126.213005 + 0.071123 + WP0008 + + + 142.812088 + 0.050291 + WP0009 + + + 146.902481 + 0.060110 + WP0010 + + + 149.121628 + 0.068536 + WP0011 + + + 142.881119 + 0.087107 + WP0012 + + + 142.041077 + 0.095042 + WP0013 + + + 144.061920 + 0.095042 + WP0014 + + + 142.445999 + 0.082855 + WP0015 + + + 149.049561 + 0.063043 + WP0016 + + + 161.409058 + 0.057025 + WP0017 + + + 175.698807 + 0.050291 + WP0018 + + + 169.120071 + 0.050291 + WP0019 + + + 148.594345 + 0.046561 + WP0020 + + + 144.216919 + 0.042504 + WP0021 + + + 151.511292 + 0.046561 + WP0022 + + + 160.463150 + 0.026882 + WP0023 + + + 180.000000 + 0.019008 + WP0024 + + + 167.798660 + 0.000000 + WP0025 + + + 169.120071 + 0.000000 + WP0026 + + + 169.120071 + 0.000000 + WP0027 + + + 0.000000 + WP0028 + + + 0.000000 + WP0029 + + + 0.167877 + WP0030 + + + 99.441742 + 0.190083 + WP0031 + + + 0.000000 + 0.170016 + WP0032 + + + 0.053764 + WP0033 + + + 0.063043 + WP0034 + + + 0.057025 + WP0035 + + + 0.057025 + WP0036 + + + 1.432199 + WP0037 + + + 24.064192 + 1.432560 + WP0038 + + + 16.541142 + 1.618836 + WP0039 + + + 15.659211 + 1.835854 + WP0040 + + + 2.446469 + WP0041 + + + 1.096733 + WP0042 + + + 42.315998 + 1.258575 + WP0043 + + + 53.605648 + 1.478501 + WP0044 + + + 30.277189 + 1.237587 + WP0045 + + + 41.205185 + 1.278231 + WP0046 + + + 42.981739 + 1.427271 + WP0047 + + + 45.288700 + 1.287806 + WP0048 + + + 37.912586 + 1.190717 + WP0049 + + + 48.789742 + 1.202943 + WP0050 + + + 42.365654 + 1.548800 + WP0051 + + + 93.847755 + 0.464052 + WP0052 + + + 1.407002 + WP0053 + + + 1.357865 + WP0054 + + + 1.291588 + WP0055 + + + 1.300648 + WP0056 + + + 1.499009 + WP0057 + + + 1.499370 + WP0058 + + + 1.419783 + WP0059 + + + 1.123262 + WP0060 + + + 1.264875 + WP0061 + + + 1.489943 + WP0062 + + + 1.015479 + WP0063 + + + 1.142083 + WP0064 + + + 1.062938 + WP0065 + + + 1.629736 + WP0066 + + + 1.364369 + WP0067 + + + 0.478995 + WP0068 + + + 172.810944 + 0.574354 + WP0069 + + + 0.638974 + WP0070 + + + 1.676724 + WP0071 + + + 0.573724 + WP0072 + + + 0.448210 + WP0073 + + + 0.410333 + WP0074 + + + 18.980536 + 1.878750 + WP0075 + + + 0.384889 + WP0076 + + + 146.881775 + 10.627611 + WP0077 + + + 130.104218 + 4.382399 + WP0078 + + + 159.542908 + 2.005446 + WP0079 + + + 159.601395 + 4.983410 + WP0080 + + + 0.750528 + WP0081 + + + 2.515643 + WP0082 + + + 1.738717 + WP0083 + + + 1.248053 + WP0084 + + + 1.219648 + WP0085 + + + 1.044767 + WP0086 + + + 0.818463 + WP0087 + + + 0.726058 + WP0088 + + + 0.552369 + WP0089 + + + 0.563878 + WP0090 + + + 0.521951 + WP0091 + + + 0.455804 + WP0092 + + + 0.436778 + WP0093 + + + 0.398269 + WP0094 + + + 0.363154 + WP0095 + + + 0.358144 + WP0096 + + + 0.341092 + WP0097 + + + 0.326479 + WP0098 + + + 3.193230 + WP0099 + + + 6.545493 + WP0100 + + + 167.346375 + 7.196805 + WP0101 + + + 170.295609 + 5.201792 + WP0102 + + + 5.426836 + WP0103 + + + 1.610220 + WP0104 + + + 2.078176 + WP0105 + + + 0.445379 + WP0106 + + + 137.402069 + 1.775321 + WP0107 + + + 4.208789 + WP0108 + + + 2.778599 + WP0109 + + + 1.977414 + WP0110 + + + 44.302502 + 2.613213 + WP0111 + + + 2.175488 + WP0112 + + + 30.543665 + 5.881074 + WP0113 + + + 3.443919 + WP0114 + + + 6.119472 + WP0115 + + + 10.757442 + 5.495544 + WP0116 + + + 26.591137 + 3.246643 + WP0117 + + + 36.030758 + 3.738654 + WP0118 + + + 59.238380 + 1.915319 + WP0119 + + + 0.720814 + WP0120 + + + 4.727779 + 0.560343 + WP0121 + + + 2.718789 + WP0122 + + + 81.836906 + 1.306607 + WP0123 + + + 0.582474 + WP0124 + + + 0.477105 + WP0125 + + + 41.664234 + 0.241189 + WP0126 + + + 0.217560 + WP0127 + + + 41.891056 + 0.133058 + WP0128 + + + 45.655930 + 0.945843 + WP0129 + + + 1.060215 + WP0130 + + + 0.484619 + WP0131 + + + 0.688249 + WP0132 + + + 0.076033 + WP0133 + + + 103.189636 + 0.046561 + WP0134 + + + 0.096924 + WP0135 + + + 0.098770 + WP0136 + + + 0.057025 + WP0137 + + + 64.432991 + 0.032923 + WP0138 + + + 0.085008 + WP0139 + + + 0.087107 + WP0140 + + + 0.063043 + WP0141 + + + 0.065847 + WP0142 + + + 1.088465 + WP0143 + + + 49.070705 + 0.000000 + WP0144 + + + 0.091161 + WP0145 + + + 0.038017 + WP0146 + + + 0.127512 + WP0147 + + + 0.085008 + WP0148 + + + 0.162407 + WP0149 + + + 0.038017 + WP0150 + + + 148.807159 + 0.050291 + WP0151 + + + 0.068536 + WP0152 + + + 0.063043 + WP0153 + + + 154.334946 + 0.046561 + WP0154 + + + 0.060110 + WP0155 + + + 0.172128 + WP0156 + + + 0.155590 + WP0157 + + + 0.093121 + WP0158 + + + 180.000000 + 0.000000 + WP0159 + + + 146.615356 + 0.019008 + WP0160 + + + 126.621498 + 0.060110 + WP0161 + + + 129.936798 + 0.046561 + WP0162 + + + 130.929291 + 0.032923 + WP0163 + + + 132.766678 + 0.000000 + WP0164 + + + 124.748962 + 0.038017 + WP0165 + + + 130.929291 + 0.026882 + WP0166 + + + 120.031418 + 0.026882 + WP0167 + + + 120.925941 + 0.082855 + WP0168 + + + 120.525436 + 0.073619 + WP0169 + + + 124.748962 + 0.063043 + WP0170 + + + 128.248459 + 0.053764 + WP0171 + + + 139.142487 + 0.038017 + WP0172 + + + 142.445953 + 0.032923 + WP0173 + + + 130.929291 + 0.026882 + WP0174 + + + 98.223473 + 0.019008 + WP0175 + + + 79.654457 + 0.019008 + WP0176 + + + 104.938911 + 0.026882 + WP0177 + + + 101.315720 + 0.019008 + WP0178 + + + 79.093140 + 0.019008 + WP0179 + + + 90.000000 + 0.019008 + WP0180 + + + 0.000000 + 0.000000 + WP0181 + + + 0.019008 + WP0182 + + + 0.038017 + WP0183 + + + 0.046561 + WP0184 + + + 0.050291 + WP0185 + + + 0.050291 + WP0186 + + + 2.063974 + 0.032923 + WP0187 + + + 61.544453 + 0.019008 + WP0188 + + + 46.065872 + 0.019008 + WP0189 + + + 16.082796 + 0.019008 + WP0190 + + + 0.000000 + WP0191 + + + 0.000000 + WP0192 + + + 0.000000 + WP0193 + + + 0.019008 + WP0194 + + + 0.026882 + WP0195 + + + 0.038017 + WP0196 + + + 0.026882 + WP0197 + + + 0.038017 + WP0198 + + + 0.042504 + WP0199 + + + 0.053764 + WP0200 + + + 0.053764 + WP0201 + + + 4.343226 + WP0202 + + + 6.788423 + WP0203 + + + 6.013606 + WP0204 + + + 5.277713 + WP0205 + + + 3.507230 + WP0206 + + + 8.370941 + WP0207 + + + 148.805801 + 8.679982 + WP0208 + + + 139.150696 + 10.451539 + WP0209 + + + 137.718918 + 7.718669 + WP0210 + + + 145.062073 + 6.406710 + WP0211 + + + 0.899237 + WP0212 + + + 153.232285 + 4.640603 + WP0213 + + + 117.705025 + 3.625063 + WP0214 + + + 105.405190 + 3.251814 + WP0215 + + + 64.787437 + 0.627275 + WP0216 + + + 7.046580 + WP0217 + + + 10.333961 + WP0218 + + + 135.240036 + 9.152427 + WP0219 + + + 134.727890 + 3.008605 + WP0220 + + + 1.994969 + WP0221 + + + 0.300548 + WP0222 + + + 9.022692 + WP0223 + + + 128.360611 + 0.487963 + WP0224 + + + 108.194084 + 0.335216 + WP0225 + + + 0.438841 + WP0226 + + + 81.360321 + 0.859589 + WP0227 + + + 6.711519 + 0.109195 + WP0228 + + + 8.182794 + WP0229 + + + 3.507539 + WP0230 + + + 5.848113 + WP0231 + + + 6.372952 + WP0232 + + + 97.715996 + 2.818878 + WP0233 + + + 133.886002 + 15.637352 + WP0234 + + + 140.055038 + 8.139894 + WP0235 + + + 125.523293 + 2.463761 + WP0236 + + + 134.839066 + 7.375820 + WP0237 + + + 151.842575 + 3.694468 + WP0238 + + + 0.962317 + WP0239 + + + 144.244812 + 3.706478 + WP0240 + + + 11.200036 + WP0241 + + + 12.827707 + WP0242 + + + 18.368883 + WP0243 + + + 126.742546 + 19.507580 + WP0244 + + + 148.218903 + 7.472546 + WP0245 + + + 2.483881 + WP0246 + + + 173.173264 + 1.303782 + WP0247 + + + 1.967664 + WP0248 + + + 29.760809 + 1.533561 + WP0249 + + + 14.154443 + WP0250 + + + 102.553291 + 7.354651 + WP0251 + + + 3.915116 + WP0252 + + + 31.289225 + 2.894576 + WP0253 + + + 11.849266 + WP0254 + + + 163.440262 + 3.120760 + WP0255 + + + 129.279663 + 2.973259 + WP0256 + + + 23.597078 + WP0257 + + + 120.761200 + 1.060176 + WP0258 + + + 0.116564 + WP0259 + + + + diff --git a/testo b/testo index 8d43ce72a..5caf0aaa8 100755 --- a/testo +++ b/testo @@ -1074,5 +1074,13 @@ compare ${TMPDIR}/waypoints.gpssim ${REFERENCE} ${PNAME} -i gpx -f ${REFERENCE}/track/tracks.gpx -o gpssim -F ${TMPDIR}/tracks.gpssim compare ${TMPDIR}/tracks.gpssim ${REFERENCE}/track +# +# WBT-200 tests +# +rm -f ${TMPDIR}/wbt-200.* +${PNAME} -i wbt-bin -f ${REFERENCE}/wbt-200.bin -o gpx -F ${TMPDIR}/wbt-200.gpx +# Remove the timestamp +grep -v time <${TMPDIR}/wbt-200.gpx >${TMPDIR}/wbt-200.gpx2 +compare ${TMPDIR}/wbt-200.gpx2 ${REFERENCE}/wbt-200.gpx exit 0 diff --git a/vecs.c b/vecs.c index c9ae47ad8..5864f74c7 100644 --- a/vecs.c +++ b/vecs.c @@ -106,7 +106,8 @@ extern ff_vecs_t vitosmt_vecs; extern ff_vecs_t wfff_xml_vecs; extern ff_vecs_t xcsv_vecs; extern ff_vecs_t yahoo_vecs; -extern ff_vecs_t wbt_vecs; +extern ff_vecs_t wbt_svecs; +extern ff_vecs_t wbt_fvecs; static vecs_t vec_list[] = { @@ -382,9 +383,15 @@ vecs_t vec_list[] = { NULL }, { - &wbt_vecs, + &wbt_svecs, "wbt", "Wintec WBT-100/200 GPS Download", + "bin" + }, + { + &wbt_fvecs, + "wbt-bin", + "Wintec WBT-100/200 Binary file format", NULL }, { diff --git a/wbt-200.c b/wbt-200.c index 585cfd75f..685031bf5 100644 --- a/wbt-200.c +++ b/wbt-200.c @@ -19,10 +19,13 @@ */ #include "defs.h" -#include "jeeps/gpsserial.h" +#include "gbser.h" #include "grtcirc.h" #include +#define BAUD 9600 +#define TIMEOUT 1500 + /* A conversation looks like this @@ -39,14 +42,22 @@ << $PFST,NORMAL,*02 */ -static gpsdevh *fd; +/*static gpsdevh *fd;*/ +static void *fd; +static FILE *fl; static char *port; static char *erase; #define MYNAME "WBT-100/200" -#define PRESTRKNAME "PRESALTTRK" #define NL "\x0D\x0A" +struct read_state { + route_head *route_head; + double plat, plon; /* previous point */ + time_t ptim; + unsigned wpn; +}; + /* Number of lines to skip while waiting for an ACK from a command. I've seen * conversations with up to 30 lines of cruft before the response so 50 isn't * too crazy. @@ -62,92 +73,63 @@ static void db(int l, const char *msg, ...) { va_end(ap); } -/* Read a single character from the serial port. Kind of gross but we do - * it like this so we can use Jeeps (which doesn't have an equivalent - * function). Returns -1 if no char is available, -2 on error or the - * retrieved char. - */ -static int rd_char() { - if (GPS_Serial_Chars_Ready(fd)) { - unsigned char c; - if (GPS_Serial_Read(fd, &c, 1) != 1) { - return -2; - } else { - return c; - } - } else { - return -1; - } -} - -/* Blocking version of above. It would be nicer to use select on the fd - * rather than spinning in a loop here - but we're trying to stay platform - * independent. - */ -static int rd_char_b() { - int c = rd_char(); - while (c == -1) { - c = rd_char(); +static void rd_drain() { + if (gbser_flush(fd)) { + fatal(MYNAME ": Comm error\n"); } - - return c; } -/* Swallow any pending output from GPS */ - -static int rd_drain() { - int c = rd_char(); - while (c >= 0) { - c = rd_char(); +static void rd_line(char *buf, int len) { + int rc; + if (rc = gbser_read_line(fd, buf, len, TIMEOUT, 0x0A, 0x0D), rc != gbser_OK) { + fatal(MYNAME ": Read error (%d)\n", rc); } - - return c == -1 ? 0 : c; } -/* Read a line (up to 0x0A). Carriage returns are filtered out. Always tries to - * read an entire line but discards any characters beyond len (because we're - * only ever interested in fairly short lines. - * - * Returns the number of characters read or -2 on error. The buffer will contain - * the (possibly truncated) string without any line terminator characters. The - * buffer will always be null terminated. - */ -static int rd_line(char *buf, int len) { - int c, pos = 0, nr = 0; - c = rd_char_b(); - while (c >= 0 && c != 0x0A) { - nr++; - if (c != 0x0D && pos < len-1) { - buf[pos++] = (unsigned char) c; - } - c = rd_char_b(); +static void wr_cmd(const char *cmd) { + int rc; + db(3, "Sending: %s\n", cmd); + if (rc = gbser_print(fd, cmd), gbser_OK != rc) { + fatal(MYNAME ": Write error (%d)\n", rc); } - - buf[pos] = '\0'; - - return c < 0 ? c : nr; -} - -static int wr_cmd(const char *cmd) { - return GPS_Serial_Write(fd, cmd, strlen(cmd)); } static void rd_init(const char *fname) { port = xstrdup(fname); db(1, "Opening port...\n"); - if (!GPS_Serial_On(port, &fd)) { - fatal(MYNAME ": Can't initialise port '%s'\n", port); + if ((fd = gbser_init(port), NULL == fd) || + gbser_set_port(fd, BAUD, 8, 0, 1)) { + fatal(MYNAME ": Can't initialise port \"%s\"\n", port); } } static void rd_deinit(void) { db(1, "Closing port...\n"); - if (!GPS_Serial_Off(fd)) { - fatal(MYNAME ": Can't shut down port '%s'\n", port); + gbser_deinit(fd); + fd = NULL; + xfree(port); +} + +static void rd_buf(void *buf, int len) { + int rc; + if (rc = gbser_read_wait(fd, buf, len, TIMEOUT), rc < 0) { + fatal(MYNAME ": Read error (%d)\n", rc); + } else if (rc < len) { + fatal(MYNAME ": Read timout\n"); } +} - xfree(port); +static void file_init(const char *fname) { + db(1, "Opening file...\n"); + if (fl = fopen(fname, "rb"), NULL == fl) { + fatal(MYNAME ": Can't open file '%s'\n", fname); + } +} + +static void file_deinit(void) { + db(1, "Closing file...\n"); + fclose(fl); } static int starts_with(const char *buf, const char *pat) { @@ -159,12 +141,9 @@ static int starts_with(const char *buf, const char *pat) { */ static void do_cmd(const char *cmd, const char *expect, char *buf, int len) { - int rc, try; - - if (rd_drain() < 0) { - fatal(MYNAME ": Read error\n"); - } + int try; + rd_drain(); wr_cmd(cmd); wr_cmd(NL); @@ -175,9 +154,7 @@ static void do_cmd(const char *cmd, const char *expect, char *buf, int len) { * middle of an NMEA sentence when we start listening. */ for (try = 0; try < RETRIES; try++) { - if (rc = rd_line(buf, len), rc < 0) { - fatal(MYNAME ": Read error\n"); - } + rd_line(buf, len); if (starts_with(buf, expect)) { db(2, "Got: %s\n", buf); return; @@ -192,16 +169,80 @@ static void do_simple(const char *cmd, char *buf, int len) { do_cmd(cmd, cmd, buf, len); } -static void rd_buf(void *buf, int len) { - char *bp = buf; +static void data_chunk(struct read_state *st, const void *buf) { + char wp_name[20]; + gbuint32 tim; + double lat, lon; + struct tm t; + time_t rtim; + waypoint *wpt = NULL; + const char *bp = buf; - while (len > 0) { - int rc = GPS_Serial_Read(fd, bp, len); - if (rc < 0) { - fatal(MYNAME ": Read error\n"); - } - len -= rc; - bp += rc; + tim = le_read32(bp + 0); + + lat = (double) ((gbint32) le_read32(bp + 4)) / 10000000; + lon = (double) ((gbint32) le_read32(bp + 8)) / 10000000; + + t.tm_sec = ((tim >> 0) & 0x3F); + t.tm_min = ((tim >> 6) & 0x3F); + t.tm_hour = ((tim >> 12) & 0x1F); + t.tm_mday = ((tim >> 17) & 0x1F); + t.tm_mon = ((tim >> 22) & 0x0F) - 1; + t.tm_year = ((tim >> 26) & 0x3F) + 100; + + rtim = mkgmtime(&t); + + if (lat >= 100) { + /* Start new track */ + lat -= 100; + st->route_head = NULL; + } else { + wpt = waypt_new(); + + wpt->latitude = lat;; + wpt->longitude = lon; + wpt->creation_time = rtim; + wpt->centiseconds = 0; + + /* OK to reuse buffer now */ + sprintf(wp_name, "WP%04d", ++st->wpn); + wpt->shortname = xstrdup(wp_name); + + wpt->speed = radtometers( + gcdist(RAD(st->plat), RAD(st->plon), + RAD(lat), RAD(lon))) / + (rtim - st->ptim); + wpt->course = DEG(heading(RAD(st->plat), RAD(st->plon), + RAD(lat), RAD(lon))); + wpt->pdop = 0; + wpt->fix = fix_unknown; + + if (NULL == st->route_head) { + db(1, "New Track\n"); + st->route_head = route_head_alloc(); + track_add_head(st->route_head); + } + + track_add_wpt(st->route_head, wpt); + } + + st->ptim = rtim; + st->plat = lat; + st->plon = lon; +} + +static void file_read(void) { + char buf[12]; + int rc; + struct read_state st; + + st.route_head = NULL; + st.wpn = 0; + + rc = fread(buf, sizeof(buf), 1, fl); + while (rc == 1) { + data_chunk(&st, buf); + rc = fread(buf, sizeof(buf), 1, fl); } } @@ -210,15 +251,12 @@ static void data_read(void) { * Actually, it's OK because rd_line can read arbitrarily * long lines returning only the first N characters */ - char line_buf[100]; - int count, d; - gbuint32 tim, ptim = 0; - double lat, lon; - double plat = 0, plon = 0; /* previous point */ - struct tm t; - time_t rtim; - route_head *route_head = NULL; - waypoint *wpt = NULL; + char line_buf[100]; + int count, d; + struct read_state st; + + st.route_head = NULL; + st.wpn = 0; do_simple("$PFST,FIRMWAREVERSION", line_buf, sizeof(line_buf)); do_simple("$PFST,NORMAL", line_buf, sizeof(line_buf)); @@ -230,61 +268,11 @@ static void data_read(void) { if (count == 0x10000) { count = 0; } + db(1, "Reading %d data\n", count); for (d = 0; d < count; d++) { rd_buf(line_buf, 12); /* twelve byte record */ - tim = le_read32(line_buf + 0); - - lat = (double) ((gbint32) le_read32(line_buf + 4)) / 10000000; - lon = (double) ((gbint32) le_read32(line_buf + 8)) / 10000000; - - t.tm_sec = ((tim >> 0) & 0x3F); - t.tm_min = ((tim >> 6) & 0x3F); - t.tm_hour = ((tim >> 12) & 0x1F); - t.tm_mday = ((tim >> 17) & 0x1F); - t.tm_mon = ((tim >> 22) & 0x0F) - 1; - t.tm_year = ((tim >> 26) & 0x3F) + 100; - - rtim = mkgmtime(&t); - - if (lat >= 100) { - /* Start new track */ - lat -= 100; - route_head = NULL; - } else { - wpt = waypt_new(); - - wpt->latitude = lat;; - wpt->longitude = lon; - wpt->creation_time = rtim; - wpt->centiseconds = 0; - - /* OK to reuse buffer now */ - sprintf(line_buf, "WP%04d", d + 1); - wpt->shortname = xstrdup(line_buf); - - wpt->speed = radtometers( - gcdist(RAD(plat), RAD(plon), - RAD(lat), RAD(lon))) / - (rtim - ptim); - wpt->course = DEG(heading(RAD(plat), RAD(plon), - RAD(lat), RAD(lon))); - wpt->pdop = 0; - wpt->fix = fix_unknown; - wpt->sat = 0; - - if (NULL == route_head) { - db(1, "New Track\n"); - route_head = route_head_alloc(); - track_add_head(route_head); - } - - track_add_wpt(route_head, wpt); - } - - ptim = rtim; - plat = lat; - plon = lon; + data_chunk(&st, line_buf); } /* Erase data? */ @@ -307,13 +295,13 @@ static void data_read(void) { } -static arglist_t wbt_args[] = { +static arglist_t wbt_sargs[] = { { "erase", &erase, "Erase device data after download", "0", ARGTYPE_BOOL, ARG_NOMINMAX }, ARG_TERMINATOR }; -ff_vecs_t wbt_vecs = { +ff_vecs_t wbt_svecs = { ff_type_serial, { ff_cap_none, ff_cap_read, ff_cap_none }, rd_init, @@ -323,6 +311,24 @@ ff_vecs_t wbt_vecs = { data_read, NULL, NULL, - wbt_args, + wbt_sargs, + CET_CHARSET_UTF8, 1 /* master process: don't convert anything | CET-REVIEW */ +}; + +static arglist_t wbt_fargs[] = { + ARG_TERMINATOR +}; + +ff_vecs_t wbt_fvecs = { + ff_type_file, + { ff_cap_none, ff_cap_read, ff_cap_none }, + file_init, + NULL, + file_deinit, + NULL, + file_read, + NULL, + NULL, + wbt_fargs, CET_CHARSET_UTF8, 1 /* master process: don't convert anything | CET-REVIEW */ }; -- 2.30.2